﻿using System;
using System.Collections.Generic;

namespace Raven
{
    public class C2DMatrix
    {
        private struct Matrix
        {
            public Fix _11, _12, _13;
            public Fix _21, _22, _23;
            public Fix _31, _32, _33;
        };

        private Matrix m_Matrix;

        //multiplies m_Matrix with mIn
        private void MatrixMultiply(Matrix mIn)
        {
            Matrix mat_temp;

            //first row
            mat_temp._11 = (m_Matrix._11 * mIn._11) + (m_Matrix._12 * mIn._21) + (m_Matrix._13 * mIn._31);
            mat_temp._12 = (m_Matrix._11 * mIn._12) + (m_Matrix._12 * mIn._22) + (m_Matrix._13 * mIn._32);
            mat_temp._13 = (m_Matrix._11 * mIn._13) + (m_Matrix._12 * mIn._23) + (m_Matrix._13 * mIn._33);

            //second
            mat_temp._21 = (m_Matrix._21 * mIn._11) + (m_Matrix._22 * mIn._21) + (m_Matrix._23 * mIn._31);
            mat_temp._22 = (m_Matrix._21 * mIn._12) + (m_Matrix._22 * mIn._22) + (m_Matrix._23 * mIn._32);
            mat_temp._23 = (m_Matrix._21 * mIn._13) + (m_Matrix._22 * mIn._23) + (m_Matrix._23 * mIn._33);

            //third
            mat_temp._31 = (m_Matrix._31 * mIn._11) + (m_Matrix._32 * mIn._21) + (m_Matrix._33 * mIn._31);
            mat_temp._32 = (m_Matrix._31 * mIn._12) + (m_Matrix._32 * mIn._22) + (m_Matrix._33 * mIn._32);
            mat_temp._33 = (m_Matrix._31 * mIn._13) + (m_Matrix._32 * mIn._23) + (m_Matrix._33 * mIn._33);

            m_Matrix = mat_temp;
        }

        public C2DMatrix()
        {
            Identity();
        }

        //create an identity matrix
        public void Identity()
        {
            m_Matrix._11 = 1;
            m_Matrix._12 = 0;
            m_Matrix._13 = 0;

            m_Matrix._21 = 0;
            m_Matrix._22 = 1;
            m_Matrix._23 = 0;

            m_Matrix._31 = 0;
            m_Matrix._32 = 0;
            m_Matrix._33 = 1;
        }

        //create a transformation matrix
        public void Translate(Fix x, Fix y)
        {
            Matrix mat;

            mat._11 = 1;
            mat._12 = 0;
            mat._13 = 0;

            mat._21 = 0;
            mat._22 = 1;
            mat._23 = 0;

            mat._31 = x;
            mat._32 = y;
            mat._33 = 1;

            //and multiply
            MatrixMultiply(mat);
        }

        //create a scale matrix
        public void Scale(Fix xScale, Fix yScale)
        {
            Matrix mat;

            mat._11 = xScale;
            mat._12 = 0;
            mat._13 = 0;

            mat._21 = 0;
            mat._22 = yScale;
            mat._23 = 0;

            mat._31 = 0;
            mat._32 = 0;
            mat._33 = 1;

            //and multiply
            MatrixMultiply(mat);
        }

        //create a rotation matrix
        public void Rotate(Fix rot)
        {
            Matrix mat;

            Fix Sin = FixMath.Sin(rot);
            Fix Cos = FixMath.Cos(rot);

            mat._11 = Cos;
            mat._12 = Sin;
            mat._13 = 0;

            mat._21 = -Sin;
            mat._22 = Cos;
            mat._23 = 0;

            mat._31 = 0;
            mat._32 = 0;
            mat._33 = 1;

            //and multiply
            MatrixMultiply(mat);
        }

        //create a rotation matrix from a fwd and side 2D vector
        public void Rotate(Vector2 fwd, Vector2 side)
        {
            Matrix mat;

            mat._11 = fwd.x;
            mat._12 = fwd.y;
            mat._13 = 0;

            mat._21 = side.x;
            mat._22 = side.y;
            mat._23 = 0;

            mat._31 = 0;
            mat._32 = 0;
            mat._33 = 1;

            //and multiply
            MatrixMultiply(mat);
        }

        //applys a transformation matrix to a std::vector of points
        public void TransformVector2s(List<Vector2> vPoints)
        {
            for (int i = 0; i < vPoints.Count; ++i)
            {
                Fix tempX = (m_Matrix._11 * vPoints[i].x) + (m_Matrix._21 * vPoints[i].y) + (m_Matrix._31);

                Fix tempY = (m_Matrix._12 * vPoints[i].x) + (m_Matrix._22 * vPoints[i].y) + (m_Matrix._32);

                vPoints[i] = new Vector2(tempX, tempY);
            }
        }

        //applys a transformation matrix to a point
        public void TransformVector2s(ref Vector2 vPoint)
        {
            Fix tempX = (m_Matrix._11 * vPoint.x) + (m_Matrix._21 * vPoint.y) + (m_Matrix._31);

            Fix tempY = (m_Matrix._12 * vPoint.x) + (m_Matrix._22 * vPoint.y) + (m_Matrix._32);

            vPoint.x = tempX;

            vPoint.y = tempY;
        }

        //accessors to the matrix elements
        public void _11(Fix val)
        {
            m_Matrix._11 = val;
        }

        public void _12(Fix val)
        {
            m_Matrix._12 = val;
        }

        public void _13(Fix val)
        {
            m_Matrix._13 = val;
        }

        public void _21(Fix val)
        {
            m_Matrix._21 = val;
        }

        public void _22(Fix val)
        {
            m_Matrix._22 = val;
        }

        public void _23(Fix val)
        {
            m_Matrix._23 = val;
        }

        public void _31(Fix val)
        {
            m_Matrix._31 = val;
        }

        public void _32(Fix val)
        {
            m_Matrix._32 = val;
        }

        void _33(Fix val)
        {
            m_Matrix._33 = val;
        }
    }
}